// someOperation your work to do // if we have some data to return use channel to pass data funcsomeOperation()error { time.Sleep(1 * time.Second) returnnil }
// anotherOperation // another work indenpendent with someOperation funcanotherOperation()error { time.Sleep(1 * time.Second) returnnil }
funcbizFunc()error { wg := sync.WaitGroup{} // sync.WatiGroup to sync goroutine wg.Add(2) // we have 2 operation to do, so we add 2 gofunc() { err := someOperation() if err != nil { // whatever handler } wg.Done() }() gofunc(){ err := anotherOperation() wg.Done() }() wg.Wait() // wait all goroutine to return // other operation depend on the two before }
// SizeWaitGroup the struct control limit of waitgroup type SizeWaitGroup struct { buf chanstruct{} // buffer to buf the current number of goroutines wg sync.WaitGroup // the real wait group }
// NewSizeWaitGroup wait group with limit funcNewSizeWaitGroup(size int) *SizeWaitGroup { if size <= 0 { size = defaultSize } return &SizeWaitGroup{ buf: make(chanstruct{}, size), // init the size of channel wg: sync.WaitGroup{}, } }
// AddWithContext // blocking if the number of goroutines has been reached func(c *SizeWaitGroup)AddWithContext(ctx context.Context)error { // select { case <-ctx.Done(): // parent goroutines call canceled or timedout or other happend return ctx.Err() case c.buf <- struct{}{}: // block if channel is full break } c.wg.Add(1) // we created a goroutine returnnil }